﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Services;

namespace hrainwebbase.ashx
{

    //协议类似socket通讯编程中的协议（类似）:
    //[4bytes]+[16bytes]+[some]+[1byte]
    //消息类型+消息长度+消息内容+验证位

    //(Web Server->Browser方向)json格式数据包 每个包类似C++中的结构体：

    //有用户上线:
    //{
    //    "type":"login",
    //    "login_name":"xiaozhi_5638",
    //    "login_time":"2014-04-15 12:34:19"
    //}

    //有用户发送消息:
    //{
    //    "type":"sendmsg",
    //    "msg":"hello world![emo:23]",
    //    "send_name":"xiaozhi_5638",
    //    "send_time":"2014-04-15 12:34:19"
    //}

    //有用户下线：
    //{
    //    "type":"logout",
    //    "logout_name":"xiaozhi_5638",
    //    "logout_time":"2014-04-15 12:34:19"
    //}

    //心跳包：
    //{
    //    "type":"heartbeat",
    //    "time":"2014-04-15 12:34:19"
    //}

    //自己登录结果:
    //{
    //    "type":"login_result",
    //    "result":"true",   //or false
    //    "online_users":["xiaozhi_5638","somebody","zhangsan"],  //登录成功 返回在线用户
    //    "time":"2014-04-15 12:34:19"
    //}

    //自己发送消息结果
    //{
    //    "type":"sendmsg_result",
    //    "result":"true",  //or  false
    //    "msg":"hello world![emo:23]",
    //    "time":"2014-04-15 12:34:19"
    //}

    //数据包集合(上面的都是单个数据包，data_list中包含多个数据包集合) 用于一次性将服务端缓存的数据包传递到browser
    //{
    //    "type":"data_list",
    //    "list":[{"type":"login","login_name":"xiaozhi_5638","login_time":"2014-04-15 12:34:19"},{"type":"sendmsg","msg":"hello world!","send_name":"xiaozhi_5638","send_time":"2014-04-15 12:34:19"}]  //数组类型 包含多个数据包
    //}


  
    public class Httpcomet : IHttpAsyncHandler
    {

        public void ProcessRequest(HttpContext context)
        {

        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        #region IHttpAsyncHandler 成员

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)  //web server调用BeginProcessRequest开始异步处理http请求
        {
            IAsyncResult ar = new chatAsyncResult(context, cb, extraData);
            if (context.Request["requestType"] == "login")  //用户登录
            {
                chatManager.Login(context.Request["id"], ar);  //处理
            }
            if (context.Request["requestType"] == "sendmsg")  //发送消息
            {
                chatManager.SendMsg(context.Request["id"], context.Request["msg"], ar); //处理
            }
            if (context.Request["requestType"] == "a_long_connection")  //开始一个http长连接
            {
                chatManager.Start_A_Long_Connection(context.Request["id"], ar); //处理  维持该http连接
            }
            return ar;
        }

        public void EndProcessRequest(IAsyncResult result)  //web server调用EndProcessRequest结束异步处理http请求  一次http连接结束
        {
            int a = 10;
        }

        #endregion
    }

    /// <summary>
    /// 唯一标示一个异步处理http请求
    /// </summary>
    class chatAsyncResult : IAsyncResult
    {
        HttpContext _context;  //与当前http请求相关的context
        AsyncCallback _cb;  //异步处理的回调方法
        object _extraData;  //开启异步处理时  传进来的附加参数

        bool _isCompleted;  //异步过程是否完成

        public chatAsyncResult(HttpContext context, AsyncCallback cb, object extraData)
        {
            _context = context;
            _cb = cb;
            _extraData = extraData;
        }
        public bool SendJson(string Json)  //向客户端发送json格式数据  可以看见 这里仍然是通过context.Response.Write的方式发送数据
        {
            try
            {

                _context.Response.Write(Json);
                if (_cb != null)
                {
                    _cb(this);   //这步非常关键  该操作告诉web server结束本次http连接 （即server-browser的一次http连接结束）
                }
                return true;
            }
            catch (Exception ee)
            {
                return false;
            }
            finally
            {
                _isCompleted = true;
            }
        }
        #region IAsyncResult 成员

        public object AsyncState
        {
            get { return null; }
        }

        public System.Threading.WaitHandle AsyncWaitHandle
        {
            get { return null; }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return _isCompleted; }
        }

        #endregion
    }
    /// <summary>
    /// 在线用户
    /// </summary>
    class chatClient
    {
        List<string> _jsons = new List<string>();  //缓存json数据
        bool _isAlive = true;  //当前http长连接是否有效
        IAsyncResult _chatAsyncResult;  //与每个client对应的异步http处理的表示
        object _syncObj = new object();
        DateTime _last_connect_time;  //上次建立长连接的时间
        public chatClient(IAsyncResult ar)
        {
            _chatAsyncResult = ar;
            _last_connect_time = DateTime.Now;
        }
        public void SendJson(string json)  //发送json数据包
        {
            lock (_syncObj)
            {
                if (_isAlive)  //当前http长连接有效  直接发送  之后http长连接失效（关闭）
                {
                    _isAlive = false;
                    (_chatAsyncResult as chatAsyncResult).SendJson(json);
                }
                else  //当前http长连接已失效  缓存json数据包
                {
                    _jsons.Add(json);
                }
            }
        }
        public bool CheckState()  //
        {
            if ((DateTime.Now - _last_connect_time).TotalSeconds > 30)  //三十秒没有新的长连接建立  则认为客户端断开
            {
                return false;
            }

            if ((DateTime.Now - _last_connect_time).TotalSeconds > 8)  //每十五秒发送一次心跳包  强制browser重新建立长连接
            {
                SendJson("{\"type\":\"heartbeat\",\"time\":\"" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + "\"}");
            }
            return true;
        }

        public void UpdateAsyncResult(IAsyncResult ar)  //重新建立http长连接
        {
            lock (_syncObj)
            {
                if (!_isAlive)
                {
                    _chatAsyncResult = ar;
                    _isAlive = true;
                    _last_connect_time = DateTime.Now;
                    if (_jsons.Count > 0)  //如果有缓存的json数据包  发送数据包集合（见msg_protocol.txt）
                    {
                        string json = "[";
                        foreach (string s in _jsons)
                        {
                            json += s + ",";
                        }
                        json = json.TrimEnd(',');
                        json += "]";

                        json = "{\"type\":\"data_list\",\"list\":" + json + "}";
                        (_chatAsyncResult as chatAsyncResult).SendJson(json);
                        _jsons.Clear();
                        _isAlive = false;
                    }
                }
            }
        }
    }
    /// <summary>
    /// chat管理
    /// </summary>
    class chatManager
    {
        static Dictionary<string, chatClient> _long_link_list = new Dictionary<string, chatClient>();  //存放每一个在线用户（类似socket模式）
        static object _syncObj = new object();

        //注意所有的json字符串中，key必须用双引号括起来
        static chatManager()
        {
            StartCheckState();
        }

        public static void Login(string id, IAsyncResult ar)  //用户登录
        {
            string t = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
            if (_long_link_list.ContainsKey(id))  //已包含该用户
            {
                string json = "{\"type\":\"login_result\",\"result\":\"false\",\"time\":\"" + t + "\"}";  //参见msg_protocol.txt文件
                (ar as chatAsyncResult).SendJson(json);  //登录失败
            }
            else
            {

                string online_usres = "[";
                string json = "{\"type\":\"login\",\"login_name\":\"" + id + "\",\"login_time\":\"" + t + "\"}";  //参见msg_protocol.txt文件
                lock (_syncObj)
                {
                    foreach (KeyValuePair<string, chatClient> p in _long_link_list)
                    {
                        online_usres += "\"" + p.Key + "\",";  //在线用户列表
                        if (p.Key != id) //排除自己在外
                            (p.Value as chatClient).SendJson(json);  //通知其他用户  同时结束各自对应的http长连接（注意这里json数据走的是之前建立的http长连接）
                    }
                }
                online_usres = online_usres.TrimEnd(',') + "]";
                json = "{\"type\":\"login_result\",\"result\":\"true\",\"online_users\":" + online_usres + ",\"time\":\"" + t + "\"}";  //参见msg_protocol.txt文件
                (ar as chatAsyncResult).SendJson(json);  //登录成功（注意这里不是走的之前建立的http长连接，而是本次http请求）

            }
        }

        public static void SendMsg(string id, string msg, IAsyncResult ar)  //发送消息
        {
            string t = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();

            string json = "{\"type\":\"sendmsg\",\"msg\":\"" + msg + "\",\"send_name\":\"" + id + "\",\"send_time\":\"" + t + "\"}";//参见msg_protocol.txt文件
            lock (_syncObj)
            {
                foreach (KeyValuePair<string, chatClient> p in _long_link_list)
                {
                    if (p.Key != id)  //排除自己在外
                        (p.Value as chatClient).SendJson(json);  //将消息发送给其他用户  同时结束各自对应的http长连接（注意这里json数据走的是之前建立的http长连接）
                }
            }

            json = "{\"type\":\"sendmsg_result\",\"result\":\"true\",\"msg\":\"" + msg + "\",\"time\":\"" + t + "\"}";//参见msg_protocol.txt文件
            (ar as chatAsyncResult).SendJson(json); //发送成功（注意这里走的不是之前建立的http长连接，而是本次http请求）
        }

        public static void Start_A_Long_Connection(string id, IAsyncResult ar)  //browser端 开始建立一个http长连接
        {
            lock (_syncObj)
            {
                if (_long_link_list.ContainsKey(id))  //如果已经存在，则更新
                {
                    _long_link_list[id].UpdateAsyncResult(ar);
                }
                else
                {
                    _long_link_list.Add(id, new chatClient(ar));  //如果不存在 属于第一次开始一个http长连接
                }
            }
            // web server将该http请求保存起来  不马上进行回复  当有必要时（有人登录/发送消息） 才回复该http请求  并结束这次“漫长的”http连接
        }

        public static void StartCheckState()  //检查在线用户的长连接状态
        {
            BackgroundWorker b = new BackgroundWorker();
            b.DoWork += new DoWorkEventHandler(b_DoWork);
            b.RunWorkerAsync();
        }

        static void b_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> deletes = new List<string>();
            while (true)
            {
                deletes.Clear();
                lock (_syncObj)
                {
                    foreach (KeyValuePair<string, chatClient> p in _long_link_list)
                    {
                        if (!p.Value.CheckState())
                        {
                            deletes.Add(p.Key);
                        }
                    }
                    foreach (string s in deletes)
                    {
                        _long_link_list.Remove(s);
                    }
                    string t = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
                    string json = "";
                    foreach (KeyValuePair<string, chatClient> p in _long_link_list)
                    {
                        foreach (string s in deletes)
                        {
                            json = "{\"type\":\"logout\",\"logout_name\":\"" + s + "\",\"logout_time\":\"" + t + "\"}";
                            p.Value.SendJson(json);  //发送下线信息
                        }
                    }
                }
                Thread.Sleep(1000); //
            }
        }
    }
}